﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Collections.ObjectModel;
using System.Data;
using System.IO;
using DbSharp.Framework.Common;

namespace DbSharp.Framework.CaseFile.Parser
{

    /// <summary>
    /// Extract all DbConnection sections from case file
    /// </summary>
    public class DbConnectionConfigExtractor
    {
        #region Private members
        private string m_CaseFullFileName;
        private XElement m_XGlobalSettingElement = null;
        private List<DbConnectionConfig> m_ListDbConnConfig = new List<DbConnectionConfig>();

        #endregion

        public DbConnectionConfigExtractor(string caseFullFileName, XElement globalSettingElement)
        {
            m_CaseFullFileName = caseFullFileName;
            m_XGlobalSettingElement = globalSettingElement;
            Extract();
        }

        #region Private methods

        /// <summary>
        /// check whether each of ConnectionName is non-empty and unique
        /// </summary>
        /// <param name="list"></param>
        private void ValidateConnectionName(IEnumerable<DbConnectionConfig> list)
        {
            var listNames = new HashSet<string>();
            foreach (var item in list)
            {
                if (item.ConnectionName.Length == 0)
                    throw new ArgumentException("DbConnection.ConnectionName cannot be empty string.");

                //check whether each of ConnectionName is unique
                if (false == listNames.Add(item.ConnectionName.ToUpper()))
                    throw new DuplicateNameException(string.Format("The ConnectionName {0} is already present. Please rename with another string.", item.ConnectionName));
            }
        }

        private IEnumerable<DbConnectionConfig> GetDbConfigs(XElement dbConnCfgRoot )
        {
            var q = from item in dbConnCfgRoot.Elements("dbConnection")
                    let connectionNameAttribute = item.Attribute("connectionName")
                    let connectionStringAttribute = item.Attribute("connectionString")
                    let providerNameAttribute = item.Attribute("providerName")
                    select new DbConnectionConfig
                    {
                        ConnectionName = connectionNameAttribute.Value.Trim(),
                        ConnectionString = connectionStringAttribute.Value.Trim(),
                        ProviderName = providerNameAttribute.Value.Trim()
                    };
            return q;
        }



        private void Extract()
        {
            m_ListDbConnConfig.Clear();
            if (m_XGlobalSettingElement == null)
                return;

            #region Get internal dbConnection
            var q = GetDbConfigs(m_XGlobalSettingElement);
            foreach (var item in q)
            {
                m_ListDbConnConfig.Add(item);
            }
            #endregion


            #region Get external dbConnection
            var externalDbFileAttr = m_XGlobalSettingElement.Attribute("dbConnectionFile");
            if (externalDbFileAttr != null)
            {
                string externalDbFile = externalDbFileAttr.Value.Trim();
                string absolutePath = Path.GetFullPath(m_CaseFullFileName);
                absolutePath = Path.GetDirectoryName(absolutePath);
                externalDbFile = Path.Combine(absolutePath, externalDbFile);
                if (File.Exists(externalDbFile))
                {
                    var xdoc = XDocument.Load(externalDbFile);
                    var p = GetDbConfigs(xdoc.Root);
                    foreach (var item in p)
                    {
                        m_ListDbConnConfig.Add(item);
                    }
                }
            }
            #endregion

            ValidateConnectionName(m_ListDbConnConfig);
        }

        #endregion


        #region Public properties/methods

        public DbConnectionConfig this[string connectionName]
        {
            get
            {
                if (string.IsNullOrEmpty(connectionName))
                    throw new ArgumentException("connectionName must be assigned.");

                string upperName = connectionName.Trim().ToUpper();
                if (upperName.Length == 0)
                    throw new ArgumentException("connectionName cannot be an empty string.");

                foreach (var item in m_ListDbConnConfig)
                {
                    if (item.ConnectionName.ToUpper() == upperName)
                        return item;
                }

                string msgFmt = "Failed to find one dbConnection which name is '{0}'.";
                string msg = string.Format(msgFmt, connectionName);
                throw new DbSharpException(msg);
            }
        }

        public ReadOnlyCollection<DbConnectionConfig> ListDbConnConfig
        {
            get
            {
                return new ReadOnlyCollection<DbConnectionConfig>(m_ListDbConnConfig);
            }
        }

        #endregion

    }

}